/* OggEnc2
 **
 ** This program is distributed under the GNU General Public License, version 2.
 ** A copy of this license is included with this source.
 **
 ** Copyright 2000-2002, Michael Smith <msmith@labyrinth.net.au>
 ** Copyright 2007, John Edwards <john.edwards33@ntlworld.com>
 **
 ** AIFF/AIFC support from OggSquish, (c) 1994-1996 Monty <xiphmont@xiph.org>
 **/


#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <math.h>

#include "audio.h"
#include "platform.h"
#include "i18n.h"
#include "samplerate.h"

#ifdef HAVE_LIBFLAC
#include "flac.h"
#endif

/* #define WAV_HEADER_SIZE 44 ---- unreferenced */

/* Macros to read header data */
#define READ_U32_LE(buf) \
	(((buf)[3]<<24)|((buf)[2]<<16)|((buf)[1]<<8)|((buf)[0]&0xff))

#define READ_U16_LE(buf) \
	(((buf)[1]<<8)|((buf)[0]&0xff))

#define READ_U32_BE(buf) \
	(((buf)[0]<<24)|((buf)[1]<<16)|((buf)[2]<<8)|((buf)[3]&0xff))

#define READ_U16_BE(buf) \
	(((buf)[0]<<8)|((buf)[1]&0xff))

static unsigned char pcm_guid[16] =
{
	/* (00000001-0000-0010-8000-00aa00389b71) */

	0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71
};

static unsigned char ieee_float_guid[16] =
{
	/* (00000003-0000-0010-8000-00aa00389b71) */

	0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71
};

/* Define the supported formats here */
input_format formats[] = {
	{wav_id, 12, wav_open, wav_close, "wav", N_("WAV file reader")},
	{wav_id, 12, wav_open, wav_close, "ape", N_("WAV file reader")},
	{wav_id, 12, wav_open, wav_close, "wv", N_("WAV file reader")},
	{wav_id, 12, wav_open, wav_close, "pac", N_("WAV file reader")},
	{wav_id, 12, wav_open, wav_close, "ofr", N_("WAV file reader")},
	{wav_id, 12, wav_open, wav_close, "shn", N_("WAV file reader")},
	{aiff_id, 12, aiff_open, wav_close, "aiff", N_("AIFF/AIFC file reader")},
#ifdef HAVE_LIBFLAC
	{flac_id,     4, flac_open, flac_close, "flac", N_("FLAC file reader")},
	{oggflac_id, 32, flac_open, flac_close, "ogg", N_("Ogg FLAC file reader")},
#endif
	{NULL, 0, NULL, NULL, NULL, NULL}
};

input_format *open_audio_file(FILE *in, oe_enc_opt *opt)
{
	int j=0;
	unsigned char *buf=NULL;
	int buf_size=0, buf_filled=0;
	int size,ret;

	while(formats[j].id_func)
	{
		size = formats[j].id_data_len;
		if(size >= buf_size)
		{
			buf = realloc(buf, size);
			buf_size = size;
		}

		if(size > buf_filled)
		{
			ret = fread(buf+buf_filled, 1, buf_size-buf_filled, in);
			buf_filled += ret;

			if(buf_filled < size)
			{ /* File truncated */
				j++;
				continue;
			}
		}

		if(formats[j].id_func(buf, buf_filled))
		{
			/* ok, we now have something that can handle the file */
			if(formats[j].open_func(in, opt, buf, buf_filled)) {
				free(buf);
				return &formats[j];
			}
		}
		j++;
	}

	free(buf);

	return NULL;
}

static int seek_forward(FILE *in, unsigned int length)
{
	if(fseek(in, length, SEEK_CUR))
	{
		/* Failed. Do it the hard way. */
		unsigned char buf[1024];
		int seek_needed = length, seeked;
		while(seek_needed > 0)
		{
			seeked = fread(buf, 1, seek_needed>1024?1024:seek_needed, in);
			if(!seeked)
				return 0; /* Couldn't read more, can't read file */
			else
				seek_needed -= seeked;
		}
	}
	return 1;
}


static int find_wav_chunk(FILE *in, char *type, unsigned int *len)
{
	unsigned char buf[8];

	while(1)
	{
		if(fread(buf,1,8,in) < 8) /* Suck down a chunk specifier */
		{
			fprintf(stderr, _("Warning: Unexpected EOF in reading WAV header\n"));
			return 0; /* EOF before reaching the appropriate chunk */
		}

		if(memcmp(buf, type, 4))
		{
			*len = READ_U32_LE(buf+4);
			if(!seek_forward(in, *len))
				return 0;

			buf[4] = 0;
			fprintf(stderr, _("Skipping chunk of type \"%s\", length %d\n"), buf, *len);
		}
		else
		{
			*len = READ_U32_LE(buf+4);
			return 1;
		}
	}
}

static int find_aiff_chunk(FILE *in, char *type, unsigned int *len)
{
	unsigned char buf[8];
	int restarted = 0;

	while(1)
	{
		if(fread(buf,1,8,in) <8)
		{
			if(!restarted) {
				/* Handle out of order chunks by seeking back to the start
				 * to retry */
				restarted = 1;
				fseek(in, 12, SEEK_SET);
				continue;
			}
			fprintf(stderr, _("Warning: Unexpected EOF in AIFF chunk\n"));
			return 0;
		}

		*len = READ_U32_BE(buf+4);

		if(memcmp(buf,type,4))
		{
			if((*len) & 0x1)
				(*len)++;

			if(!seek_forward(in, *len))
				return 0;
		}
		else
			return 1;
	}
}



double read_IEEE80(unsigned char *buf)
{
	int s=buf[0]&0xff;
	int e=((buf[0]&0x7f)<<8)|(buf[1]&0xff);
	double f=((unsigned long)(buf[2]&0xff)<<24)|
		((buf[3]&0xff)<<16)|
		((buf[4]&0xff)<<8) |
		 (buf[5]&0xff);

	if(e==32767)
	{
		if(buf[2]&0x80)
			return HUGE_VAL; /* Really NaN, but this won't happen in reality */
		else
		{
			if(s)
				return -HUGE_VAL;
			else
				return HUGE_VAL;
		}
	}

	f=ldexp(f,32);
	f+= ((buf[6]&0xff)<<24)|
		((buf[7]&0xff)<<16)|
		((buf[8]&0xff)<<8) |
		 (buf[9]&0xff);

	return ldexp(f, e-16446);
}

/* AIFF/AIFC support adapted from the old OggSQUISH application */
int aiff_id(unsigned char *buf, int len)
{
	if(len<12) return 0; /* Truncated file, probably */

	if(memcmp(buf, "FORM", 4))
		return 0;

	if(memcmp(buf+8, "AIF",3))
		return 0;

	if(buf[11]!='C' && buf[11]!='F')
		return 0;

	return 1;
}

int aiff_open(FILE *in, oe_enc_opt *opt, unsigned char *buf, int buflen)
{
	int aifc; /* AIFC or AIFF? */
	unsigned int len;
	unsigned char *buffer;
	unsigned char buf2[8];
	aiff_fmt format;
	aifffile *aiff = malloc(sizeof(aifffile));
	int i;

	if(buf[11]=='C')
		aifc=1;
	else
		aifc=0;

	if(!find_aiff_chunk(in, "COMM", &len))
	{
		fprintf(stderr, _("Warning: No common chunk found in AIFF file\n"));
		return 0; /* EOF before COMM chunk */
	}

	if(len < 18) 
	{
		fprintf(stderr, _("Warning: Truncated common chunk in AIFF header\n"));
		return 0; /* Weird common chunk */
	}

	buffer = alloca(len);

	if(fread(buffer,1,len,in) < len)
	{
		fprintf(stderr, _("Warning: Unexpected EOF in reading AIFF header\n"));
		return 0;
	}

	format.channels = READ_U16_BE(buffer);
	format.totalframes = READ_U32_BE(buffer+2);
	format.samplesize = READ_U16_BE(buffer+6);
	format.rate = (int)read_IEEE80(buffer+8);

	aiff->bigendian = 1;

	if(aifc)
	{
		if(len < 22)
		{
			fprintf(stderr, _("Warning: AIFF-C header truncated.\n"));
			return 0;
		}

		if(!memcmp(buffer+18, "NONE", 4)) 
		{
			aiff->bigendian = 1;
		}
		else if(!memcmp(buffer+18, "sowt", 4)) 
		{
			aiff->bigendian = 0;
		}
		else
		{
			fprintf(stderr, _("Warning: Can't handle compressed AIFF-C (%c%c%c%c)\n"), *(buffer+18), *(buffer+19), *(buffer+20), *(buffer+21));
			return 0; /* Compressed. Can't handle */
		}
	}

	if(!find_aiff_chunk(in, "SSND", &len))
	{
		fprintf(stderr, _("Warning: No SSND chunk found in AIFF file\n"));
		return 0; /* No SSND chunk -> no actual audio */
	}

	if(len < 8) 
	{
		fprintf(stderr, _("Warning: Corrupted SSND chunk in AIFF header\n"));
		return 0; 
	}

	if(fread(buf2,1,8, in) < 8)
	{
		fprintf(stderr, _("Warning: Unexpected EOF reading AIFF header\n"));
		return 0;
	}

	format.offset = READ_U32_BE(buf2);
	format.blocksize = READ_U32_BE(buf2+4);

	if( format.blocksize == 0 &&
		(format.samplesize == 16 || format.samplesize == 8))
	{
		/* From here on, this is very similar to the wav code. Oh well. */
		
		opt->rate = format.rate;
		opt->channels = format.channels;
		opt->read_samples = wav_read; /* Similar enough, so we use the same */
		opt->total_samples_per_channel = format.totalframes;

		aiff->f = in;
		aiff->samplesread = 0;
		aiff->channels = format.channels;
		aiff->samplesize = format.samplesize;
		aiff->totalsamples = format.totalframes;

		opt->readdata = (void *)aiff;

		aiff->channel_permute = malloc(aiff->channels * sizeof(int));
		/* Use a default 1-1 mapping, not sure what the spec says */
		for (i=0; i < aiff->channels; i++)
			aiff->channel_permute[i] = i;


		seek_forward(in, format.offset); /* Swallow some data */
		return 1;
	}
	else
	{
		fprintf(stderr, 
				_("Warning: OggEnc does not support this type of AIFF/AIFC file\n"
				" Must be 8 or 16 bit PCM.\n"));
		return 0;
	}
}

int wav_id(unsigned char *buf, int len)
{
	unsigned int flen;
	
	if(len<12) return 0; /* Something screwed up */

	if(memcmp(buf, "RIFF", 4))
		return 0; /* Not wave */

	flen = READ_U32_LE(buf+4); /* We don't use this */

	if(memcmp(buf+8, "WAVE",4))
		return 0; /* RIFF, but not wave */

	return 1;
}

static int wav_permute_matrix[6][6] = 
{
	{0},
	{0,1},
	{0,2,1},
	{0,1,2,3},
	{0,2,1,3,4},
	{0,2,1,4,5,3}
};

int wav_open(FILE *in, oe_enc_opt *opt, unsigned char *oldbuf, int buflen)
{
	unsigned char buf[41];
	unsigned int len;
	int samplesize;
	wav_fmt format;
	wavfile *wav = malloc(sizeof(wavfile));
	int i;

	/* Ok. At this point, we know we have a WAV file. Now we have to detect
	 * whether we support the subtype, and we have to find the actual data
	 * We don't (for the wav reader) need to use the buffer we used to id this
	 * as a wav file (oldbuf)
	 */
	if(!find_wav_chunk(in, "fmt ", &len))
		return 0; /* EOF */

	if(len < 16) {
		fprintf(stderr, _("Warning: Unrecognised format chunk in WAV header\n"));
		return 0; /* Weird format chunk */
	}

	/* A common error is to have a format chunk that is not 16 or 18 bytes
	 * in size.  This is incorrect, but not fatal, so we only warn about 
	 * it instead of refusing to work with the file.  Please, if you
	 * have a program that's creating format chunks of sizes other than
	 * 16, 18 or 40 bytes in size, report a bug to the author.
	 * (40 bytes accommodates WAVEFORMATEXTENSIBLE conforming files.)
	 */
	if (len!=16 && len!=18 && len!=40)
		fprintf(stderr, _("Warning: INVALID format chunk in wav header.\n"
				" Trying to read anyway (may not work)...\n"));

	/* Deal with stupid broken apps. Don't use these programs.
	 */
	if (fread(buf,1,len,in) < len) {
		fprintf(stderr, _("Warning: Unexpected EOF in reading WAV header\n"));
		return 0;
	}

	format.format =      READ_U16_LE(buf); 
	format.channels =    READ_U16_LE(buf+2); 
	format.samplerate =  READ_U32_LE(buf+4);
	format.bytespersec = READ_U32_LE(buf+8);
	format.align =       READ_U16_LE(buf+12);
	format.samplesize =  READ_U16_LE(buf+14);

	if(format.format == WAVE_FORMAT_PCM) {
		samplesize = format.samplesize/8;
		opt->read_samples = wav_read;
		wav->channel_map = UNDEFINED;
	}
	else if(format.format == WAVE_FORMAT_IEEE_FLOAT) {
		samplesize = 4;
		opt->read_samples = wav_ieee_read;
		wav->channel_map = UNDEFINED;
	}
	else if (format.format == WAVE_FORMAT_EXTENSIBLE) {
		format.channel_mask = READ_U32_LE(buf+20);
		if (format.channel_mask == SPEAKER_FRONT_LEFT)
			wav->channel_map = DEFINED;
		else if (format.channel_mask == (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT))
			wav->channel_map = DEFINED;
		else if (format.channel_mask == (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER))
			wav->channel_map = DEFINED;
		else if (format.channel_mask == (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT))
			wav->channel_map = DEFINED;
		else if (format.channel_mask == (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER
		                               | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT))
			wav->channel_map = DEFINED;
		else if (format.channel_mask == (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER
		                               | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT))
			wav->channel_map = DEFINED;
		else
			wav->channel_map = UNDEFINED;
		if (memcmp(buf+24, pcm_guid, 16) == 0) {
			samplesize = format.samplesize/8;
			opt->read_samples = wav_read;
		}
		else if (memcmp(buf+24, ieee_float_guid, 16) == 0) {
			samplesize = 4;
			opt->read_samples = wav_ieee_read;
		}
		else {
			fprintf(stderr, _("ERROR: Wav file is unsupported type (must be standard PCM\n"
					" or type 3 floating point PCM)(2)\n"));
			return 0;
		}
	}
	else {
		fprintf(stderr, _("ERROR: Wav file is unsupported type (must be standard PCM\n"
				" or type 3 floating point PCM\n"));
		return 0;
	}

	if(!find_wav_chunk(in, "data", &len))
		return 0; /* EOF */

	if(format.align != format.channels * samplesize) {
		/* This is incorrect according to the spec. Warn loudly, then ignore
		 * this value.
		 */
		fprintf(stderr, _("Warning: WAV 'block alignment' value is incorrect, ignoring.\n" 
		    "The software that created this file is incorrect.\n"));
	}
	if(format.samplesize == samplesize*8 && 
		(format.samplesize == 32 || format.samplesize == 24 ||
		 format.samplesize == 16 || format.samplesize == 8)) {
		/* OK, good - we have a supported format,
		 * now we want to find the size of the file
		 */
		opt->rate = format.samplerate;
		opt->channels = format.channels;

		wav->f = in;
		wav->samplesread = 0;
		wav->bigendian = 0;
		wav->channels = format.channels; /* This is in several places. The price
							of trying to abstract stuff. */
		wav->samplesize = format.samplesize;

		if(len)
			opt->total_samples_per_channel = len/(format.channels*samplesize);
		else {
			long pos;
			pos = ftell(in);
			if(fseek(in, 0, SEEK_END) == -1)
				opt->total_samples_per_channel = 0; /* Give up */
			else {
				opt->total_samples_per_channel = (ftell(in) - pos)/(format.channels*samplesize);
				fseek(in,pos, SEEK_SET);
			}
		}
		wav->totalsamples = opt->total_samples_per_channel;

		opt->readdata = (void *)wav;

		wav->channel_permute = malloc(wav->channels * sizeof(int));
		if (wav->channel_map)
			/* Where we know the mappings, use them. */
			memcpy(wav->channel_permute, wav_permute_matrix[wav->channels-1], sizeof(int) * wav->channels);
		else
			/* Use a default 1-1 mapping */
			for (i=0; i < wav->channels; i++)
				wav->channel_permute[i] = i;

		return 1;
	}
	else {
		fprintf(stderr, _("ERROR: Wav file is unsupported subformat (must be 8, 16, 24 or 32 bit PCM\n"
				"or floating point PCM\n"));
		return 0;
	}
}

long wav_read(void *in, float **buffer, int samples)
{
	wavfile *f = (wavfile *)in;
	int sampbyte = f->samplesize / 8;
	signed char *buf = alloca(samples*sampbyte*f->channels);
	long bytes_read = fread(buf, 1, samples*sampbyte*f->channels, f->f);
	int i,j;
	long realsamples;
	int *ch_permute = f->channel_permute;

	if (f->totalsamples && f->samplesread + bytes_read/(sampbyte*f->channels) > f->totalsamples)
	{
		bytes_read = sampbyte*f->channels*(f->totalsamples - f->samplesread);
	}

	realsamples = bytes_read/(sampbyte*f->channels);
	if (f->totalsamples)
		f->samplesread += realsamples;

	if (f->samplesize==8)
	{
		unsigned char *bufu = (unsigned char *)buf;
		for (i = 0; i < realsamples; i++)
		{
			for (j=0; j < f->channels; j++)
			{
				buffer[j][i]=((int)(bufu[i*f->channels + ch_permute[j]])-128)/128.0f;
			}
		}
	}
	else if (f->samplesize==16)
	{
		if(!f->bigendian)
		{
			for(i = 0; i < realsamples; i++)
			{
				for(j=0; j < f->channels; j++)
				{
					buffer[j][i] = ((buf[i*2*f->channels + 2*ch_permute[j] + 1]<<8) |
							(buf[i*2*f->channels + 2*ch_permute[j]] & 0xff))/32768.0f;
				}
			}
		}
		else
		{
			for(i = 0; i < realsamples; i++)
			{
				for(j=0; j < f->channels; j++)
				{
					buffer[j][i] = ((buf[i*2*f->channels + 2*ch_permute[j]]<<8) |
							(buf[i*2*f->channels + 2*ch_permute[j] + 1] & 0xff))/32768.0f;
				}
			}
		}
	}
	else if (f->samplesize==24)
	{
		if (!f->bigendian)
		{
			for (i = 0; i < realsamples; i++)
			{
				for (j=0; j < f->channels; j++)
				{
					buffer[j][i] = 	 	  ((buf[i*3*f->channels + 3*ch_permute[j] + 2] << 16) |
						(((unsigned char *)buf)[i*3*f->channels + 3*ch_permute[j] + 1] << 8) |
						(((unsigned char *)buf)[i*3*f->channels + 3*ch_permute[j]] & 0xff)) 
						/ 8388608.0f;
				}
			}
		}
		else
		{
			fprintf(stderr, _("Big endian 24 bit PCM data is not currently "
				"supported, aborting.\n"));
			return 0;
		}
	}
	else if (f->samplesize==32)
	{
		if (!f->bigendian)
		{
			for (i = 0; i < realsamples; i++)
			{
				for (j=0; j < f->channels; j++)
					buffer[j][i] = 		  ((buf[i*4*f->channels + 4*ch_permute[j] + 3] << 24) |
						(((unsigned char *)buf)[i*4*f->channels + 4*ch_permute[j] + 2] << 16) |
						(((unsigned char *)buf)[i*4*f->channels + 4*ch_permute[j] + 1] << 8) |
						(((unsigned char *)buf)[i*4*f->channels + 4*ch_permute[j]] & 0xff)) 
						/ 2147483648.0f;
			}
		}
		else {
			fprintf(stderr, _("Big endian 32 bit PCM data is not currently "
				"supported, aborting.\n"));
			return 0;
		}
	}
	else {
		fprintf(stderr, _("Internal error: attempt to read unsupported "
			"bitdepth %d\n"), f->samplesize);
		return 0;
	}

	return realsamples;
}

long wav_ieee_read(void *in, float **buffer, int samples)
{
	wavfile *f = (wavfile *)in;
	float *buf = alloca(samples*4*f->channels); /* de-interleave buffer */
	long bytes_read = fread(buf,1,samples*4*f->channels, f->f);
	int i,j;
	long realsamples;


	if (f->totalsamples && f->samplesread + bytes_read/(4*f->channels) > f->totalsamples)
		bytes_read = 4*f->channels*(f->totalsamples - f->samplesread);
	realsamples = bytes_read/(4*f->channels);
	if (f->totalsamples)
		f->samplesread += realsamples;

	for (i=0; i < realsamples; i++)
		for (j=0; j < f->channels; j++)
			buffer[j][i] = buf[i*f->channels + f->channel_permute[j]];

	return realsamples;
}


void wav_close(void *info)
{
	wavfile *f = (wavfile *)info;
	free(f->channel_permute);

	free(f);
}

int raw_open(FILE *in, oe_enc_opt *opt, unsigned char *buf, int buflen)
{
	wav_fmt format; /* fake wave header ;) */
	wavfile *wav = malloc(sizeof(wavfile));
	int i;

	/* construct fake wav header ;) */
	format.format =      opt->format; 
	format.channels =    opt->channels;
	format.samplerate =  opt->rate;
	format.samplesize =  opt->samplesize;
	format.bytespersec = opt->channels * opt->rate * opt->samplesize / 8;
	format.align =       format.bytespersec;

	wav->f =             in;
	wav->samplesread =   0;
	wav->bigendian =     opt->endianness;
	wav->channels =      format.channels;
	wav->channel_map =   UNDEFINED;
	wav->samplesize =    opt->samplesize;
	wav->totalsamples =  0;
	wav->channel_permute = malloc(wav->channels * sizeof(int));
	for (i=0; i < wav->channels; i++)
		wav->channel_permute[i] = i;

	if (opt->format == WAVE_FORMAT_IEEE_FLOAT)
		opt->read_samples = wav_ieee_read;
	else
		opt->read_samples = wav_read;
	opt->readdata = (void *)wav;
	opt->total_samples_per_channel = 0; /* raw mode, don't bother */
	return 1;
}

typedef struct {
	audio_read_func real_reader;
	SRC_STATE *src_state;
	SRC_DATA  src_data;
	void *real_readdata;
	float **buffs;
	float *in_sample_buffer;
	float *out_sample_buffer;
	long read_size;
	int converter;
	int channels;
} resampler;

long read_resampled(void *d, float **buffer, int samples)
{
	resampler *rs = d;
	int       error;
	long      in_samples;
	int       i, ch;

	in_samples = rs->real_reader(rs->real_readdata, rs->buffs, rs->read_size);

	for (ch = 0; ch < rs->channels; ch++)
		for (i = 0; i < in_samples; i++)
			rs->in_sample_buffer[(i*rs->channels)+ch] = rs->buffs[ch][i];

	rs->src_data.input_frames = in_samples;
	rs->src_data.data_in = rs->in_sample_buffer;

	if (rs->src_data.input_frames < rs->read_size)
		rs->src_data.end_of_input = 1;

	//if ((error = src_process (rs->src_state, &rs->src_data))) {
		//fprintf(stderr, _("Error : %s\n"), src_strerror (error));
		//return -1;
	//}

	if (rs->src_data.end_of_input && rs->src_data.output_frames_gen == 0)
		return 0;

	for (i=0; i < rs->src_data.output_frames_gen; i++)
		for (ch=0; ch < rs->channels; ch++)
			buffer[ch][i] = rs->out_sample_buffer[i * rs->channels + ch];

	return rs->src_data.output_frames_gen;
}

int setup_resample(oe_enc_opt *opt) {
	resampler *rs = calloc(1, sizeof(resampler));
	int c;
	int error;
	int output_len;

	rs->src_state = calloc(1, sizeof(rs->src_state));
	rs->real_reader = opt->read_samples;
	rs->buffs = malloc(sizeof(float *) * opt->channels);
	rs->real_readdata = opt->readdata;
	rs->channels = opt->channels;
	rs->converter = opt->converter;
	rs->src_data.src_ratio = opt->ratio;
	rs->src_data.input_frames = 0;
	rs->src_data.end_of_input = 0;

	if (rs->src_data.src_ratio >= 1.0) {
		rs->read_size = ((int)(floor(READSIZE / rs->src_data.src_ratio)) >> 1 << 1);
		output_len = READSIZE;
	}
	else {
		rs->read_size = READSIZE;
		output_len = (int)floor(READSIZE * rs->src_data.src_ratio);
	}

	/* Reduce input_len by 10 so output is longer than necessary. */
	rs->read_size -= 10;
		
	/* Initialize the sample rate converter. */
	//if ((rs->src_state = src_new (rs->converter, rs->channels, &error)) == NULL) {
	//	fprintf(stderr, _("Error : src_new() failed : %s.\n"), strerror (error));
	//	return -1;
	//}

	for (c = 0; c < rs->channels; c++)
		rs->buffs[c] = malloc(rs->read_size * sizeof(float));
	rs->in_sample_buffer = malloc(sizeof(float) * rs->read_size * rs->channels);
	rs->out_sample_buffer = malloc(sizeof(float) * output_len * rs->channels);
	rs->src_data.data_out = rs->out_sample_buffer;
	rs->src_data.output_frames = output_len;

	opt->read_samples = read_resampled;
	opt->readdata = rs;
	if (opt->total_samples_per_channel)
		opt->total_samples_per_channel = (int)((float)opt->total_samples_per_channel * 
			((float)opt->resamplefreq/(float)opt->rate));
	opt->rate = opt->resamplefreq;

	return 0;
}

void clear_resample(oe_enc_opt *opt) {
	resampler *rs = opt->readdata;
	int i;

	opt->read_samples = rs->real_reader;
	opt->readdata = rs->real_readdata;

	for (i = 0; i < rs->channels; i++)
		free(rs->buffs[i]);

	free(rs->buffs);
	free(rs->in_sample_buffer);
	free(rs->out_sample_buffer);
	//rs->src_state = src_delete (rs->src_state);
	free(rs->src_state);

	free(rs);
}

typedef struct {
    audio_read_func real_reader;
    void *real_readdata;
    int channels;
    float scale_factor;
} scaler;

static long read_scaler(void *data, float **buffer, int samples) {
    scaler *d = data;
    long in_samples = d->real_reader(d->real_readdata, buffer, samples);
    int i,j;

    for(i=0; i < d->channels; i++) {
        for(j=0; j < in_samples; j++) {
            buffer[i][j] *= d->scale_factor;
        }
    }

    return in_samples;
}


void setup_scaler(oe_enc_opt *opt, float scale) {
    scaler *d = calloc(1, sizeof(scaler));

    d->real_reader = opt->read_samples;
    d->real_readdata = opt->readdata;

    opt->read_samples = read_scaler;
    opt->readdata = d;
    d->channels = opt->channels;
    d->scale_factor = scale;
}

void clear_scaler(oe_enc_opt *opt) {
    scaler *d = opt->readdata;

    opt->read_samples = d->real_reader;
    opt->readdata = d->real_readdata;

    free(d);
}

typedef struct {
    audio_read_func real_reader;
    void *real_readdata;
    float **bufs;
} downmix;

long read_downmix(void *data, float **buffer, int samples)
{
    downmix *d = data;
    long in_samples = d->real_reader(d->real_readdata, d->bufs, samples);
    int i;

    for(i=0; i < in_samples; i++) {
        buffer[0][i] = (d->bufs[0][i] + d->bufs[1][i])*0.5;
    }

    return in_samples;
}

void setup_downmix(oe_enc_opt *opt) {
    downmix *d = calloc(1, sizeof(downmix));

    if(opt->channels != 2) {
        fprintf(stderr, "Internal error! Please report this bug.\n");
        return;
    }
    
    d->bufs = malloc(2 * sizeof(float *));
    d->bufs[0] = malloc(4096 * sizeof(float));
    d->bufs[1] = malloc(4096 * sizeof(float));
    d->real_reader = opt->read_samples;

    d->real_readdata = opt->readdata;

    opt->read_samples = read_downmix;
    opt->readdata = d;

    opt->channels = 1;
}
void clear_downmix(oe_enc_opt *opt) {
    downmix *d = opt->readdata;

    opt->read_samples = d->real_reader;
    opt->readdata = d->real_readdata;
    opt->channels = 2; /* other things in cleanup rely on this */

    free(d->bufs[0]);
    free(d->bufs[1]);
    free(d->bufs);
    free(d);
}

